// 写ts 语法
// ts 是一门强类型语言
// 数据类型:

// 01: number 类型
let a: number = 100;
// console.log(a);
//02: boolean 
let b: boolean = true
// console.log(b);
// 03: string
let c: string = '100'
// 04: null
let d: null = null;
//05:undefined 
let e: undefined = undefined;
//06: void 类型, 表示没有类型
// 一般用于函数的返回值
function fn(): void {
    return
}

const fn1 = (): void => {

}

//07: any 类型
// 任何类型 值可以为任意类型
let f: any = [{}]

// 08: unknown 未知类型
// 当你使用一个unkonwn 类型的时候, 开发者不知道后面值是什么类型,
// 这时候就使用unknown
let g: unknown = ''

// 09: never 类型
// 表示从来不会出现的类型
// function loop(): never {
//     while (true) { }
// }

// 10. 数组类型
// 声明数组的第一种方式: 字面量方式
const arr: string[] = ['a', 'b'];
// 声明数组的第二种方式: 泛型方式
const arr1: Array<any> = [{}, true, '', []]

//11. 元祖类型
// 特殊的数组类型
let arr3: [string, object, Array<any>] = ['', {}, []]

//12.object 类型
let obj: object = {
    name: ''
}

// 13. 枚举类型
// 枚举类型 表示将类型约定在一个范围内, 使用类型时只能使用枚举范围内的类型
// enum Sex {
//     male = '男',
//     female = '女',
//     unknown = '未知'
// }

// console.log('Sex', Sex);

// let zhaozhilong: Sex = Sex.male
// console.log('zhaozhilong', zhaozhilong);

// 14. 联合类型
let h: string | number | object = 100

// 15.Symbol类型
// 该类型是es6新增的一个类型, 特点: 不可以变且唯一
// 一般作为对象的key 使用
// let sym1 = Symbol();
// let sym2 = Symbol();

// console.log(sym1 == sym2);  // false
// let obj1 = {
//     [sym2]: '张三'
// }

// console.log(obj1[sym2]);

// 16. ts中使用函数
// 函数参数的类型 有3类
// 01: 可选参数  表示该实参可传可不传
// 02: 默认参数  表示如果实参不传, 形参就是使用默认值
// 03: 剩余参数  表示当实参的数量大于形参的数量,就会将剩余参数统一放在一个数组中接收
function fn2(m: string, n?: number, k: Array<any> = ['1', 100], ...arr5: any[]): string {
    //console.log('k', k);
    // console.log('arr5', arr5);

    return m
}

// 调用函数
// fn2('小丽')
// fn2('小丽', undefined, ['a', 'b'])
fn2('a', 10, [1, 2, 3], '安琛', 18, '男', '女')


// 17.使用interface 接口来创建数据类型
// enum Sex {
//     male = '男',
//     female = '女',
//     unknown = '未知'
// }

// // 创建了一个 Person 的数据类型
// interface Person {
//     name: string,
//     age: number,
//     sex: Sex
// }

// let p: Person = {
//     name: '张三',
//     age: 20,
//     sex: Sex.male,
// }
// console.log('p', p);


// 18: 接口的属性的类型
// 01: 可选属性 表示赋值时, 可以不用给该属性赋值
// 02: 只读属性  表示该属性只能访问不能修改
// interface Women {
//     readonly name: string,
//     age?: number,
// }


// let lishuang: Women = {
//     name: '李爽',
//     age: 18
// }
// console.log(lishuang.name);
// lishuang.age = 30


// 19. 使用接口来定义一个函数类型
// interface Fn {
//     (m: string, n: number): string[]
// }

// let f1: Fn = (m: string, n: number) => {
//     return ['1', '2', m]
// }


// interface Fn1 {
//     (): void
// }let f2: Fn1 = () => {
//     console.log(123);
// }

// 20:接口中使用函数

// interface Men {
//     name: string,
//     age: number,
//     // say(): void
//     say: () => void
// }

// let znegzeng: Men = {
//     name: '张增增',
//     age: 20,
//     say(): void {
//         console.log(this.name);

//     }
// }

//21. 可索引接口
// 该接口主要用来定义数组和对象的
//  使用interface 定义一个数组类型
// interface newArray {
//     [name: number]: string
// }

// let newarr1: newArray = ['1', '2', 'true']

// 使用interface 定义一个对象类型
// interface newObj {
//     [name: string]: any
// }

// let newobj1: newObj = {
//     name: '小红帽'
// }


// 22. 接口的继承
// 定义父接口
// interface Father {
//     name: string,
//     age: number,
//     work: () => string
// }

// // 定义子接口
// enum Sex {
//     male = '男',
//     female = '女'
// }
// interface Son extends Father {
//     height: string,
//     sex: Sex,
//     study(): string
// }

// let fanyu: Son = {
//     height: '180cm',
//     sex: Sex.male,
//     study(): string {
//         return `${this.name} 要好好写项目`
//     },
//     name: '范昱',
//     age: 19,
//     work() {
//         return `${this.name}要好好工作,3年后娶妻生子`
//     }
// }

// console.log(fanyu.study());


// 23. 使用接口定义额外属性, 可扩展属性

// interface Father {
//     name: string,
//     age: number,
//     work: () => string,
//     [key: string]: any  // 可扩展属性
// }

// let chenglong: Father = {
//     name: '成龙',
//     age: 60,
//     work() {
//         return `我要好好拍电影`
//     },
//     sex: '男',
//     like: '大妈',
//     height: 165
// }


// 25:类型断言
// 定义:有时候你比程序更知道某一个变量的类型, 如下 someValue 就是string 类型
//所以在使用该变量是, 可以使用类型断言, 变量 as 数据类型 就可以, 告诉程序该变量是什么类型

// let someValue: unknown = "this is a string";

// let strLength: number = (someValue as string).length;


// 26. 使用type 来定义数据类型
// type abc = string;
// let str: abc = '100'

// type newObj1 = {
//     name: string,
//     age: number,
//     say(m: string): string
// }

// let huijuanlaoshi: newObj1 = {
//     name: '会娟老师',
//     age: 20,
//     say(m: string): string {
//         return ``
//     }
// }

// type newArr1 = [string, number];
// const arr100: newArr1 = ['', 1]


// 面试题:
// type 和 interface 区别?
// 相同点: 都可以去创建或定义数据类型
// 不同点:
// 01: type 可以定义任意数据类型 , interface 只能定义对象数据类型
// 02: interfece 可以实现继承 而type 不能实现继承



// 27. ts中的类
// es5 中使用类创建实例对象
// function Girl(name, age) {
//     this.name = name
//     this.age = age
// }

// let fanbingbing = new Girl('范冰冰', 45);
// console.log(fanbingbing);


// es6中语法创建实例对象

// class Girl {
//     name: string;
//     age: number;
//     // 构造器
//     constructor(name: string, age: number) {
//         this.name = name
//         this.age = age
//     }
// }

// let bingbing = new Girl('范冰冰', 38)
// console.log(bingbing);



// 28. 类的继承
//定义父类

// class Animal {
//     name: string;
//     age: number;
//     eat(m: string): string {
//         return `${this.name}吃 ${m} `
//     };
//     constructor(name: string, age: number) {
//         this.name = name
//         this.age = age
//     }
// }
// // 定义子类
// class Tiger extends Animal {
//     legs: number;
//     sex: string;
//     run(): string {
//         return `${this.name}喜欢奔跑`
//     };
//     constructor(legs: number, sex: string, name: string, age: number) {
//         super(name, age)
//         this.legs = legs
//         this.sex = sex
//     }
// }


// let laohu = new Tiger(4, '雄', '老虎', 10)

// console.log(laohu);
// console.log(laohu.run());
// console.log(laohu.eat('肉'));



//29. 静态属性
// 实例属性 可以通过实例对象访问
// 静态属性 只能通过构造函数名访问

// es5 中的静态属性
// function Girl(name, age) {
//     this.name = name  // 实例属性
//     this.age = age   // 实例属性
// }
// Girl.sex = '女'  // sex 为静态属性

// let fanbingbing = new Girl('范冰冰', 45);
// console.log(fanbingbing);

// console.log(Girl.sex);


// class Girl {
//     name: string;
//     age: number;
//     static sex: string;  // 静态属性
//     // 构造器
//     constructor(name: string, age: number, sex: string) {
//         this.name = name
//         this.age = age
//         Girl.sex = sex
//     }
// }

// let bingbing = new Girl('范冰冰', 38, '女')
// console.log(bingbing);
// console.log(Girl.sex);


// 30:类类型接口
// 定义 使用类去实现一个接口, 其实就是使用接口去约束类成员属性和方法
// interface monitor {
//     name: string,
//     age: number,
//     work(): string
// }
// // implements 实现的意思
// class zhaojian implements monitor {
//     name: string;
//     age: number;
//     work() {
//         return ``
//     }
//     sex = '男'
//     constructor(name: string, age: number) {
//         this.name = name
//         this.age = age
//     }
// }

// let zhaoge = new zhaojian('赵健', 20)
// console.log(zhaoge);


// 31: 抽象类
// 抽象类 一般作为基类使用, 一般不去实例化 , 作为被继承的父类使用
// abstract class Animal {
//     name: string;
//     age: number;
//     abstract eat(m: string): string  // 抽象类中的抽象方法
//     constructor(name: string, age: number) {
//         this.name = name
//         this.age = age
//     }
// }

// // 定义子类
// class Tiger extends Animal {
//     legs: number;
//     sex: string;
//     eat(m: string): string {  //' 在继承的子类中重新实现抽象类中的抽象方法
//         return `${this.name}吃${m}`
//     }
//     run(): string {
//         return `${this.name}喜欢奔跑`
//     };
//     constructor(legs: number, sex: string, name: string, age: number) {
//         super(name, age)
//         this.legs = legs
//         this.sex = sex
//     }
// }


// 33. 泛型
// 泛型定义:使用一个变量表示类型, 传什么类型, 变量就是什么类型
//案例:
// function f10(x: number) {
//     console.log(x);

// }

// function f11(x: string) {
//     console.log(x);

// }

// 以上两个函数可以整合在一起
// function f12(x: number | string) {
//     console.log(x);
// }

// 以上可以使用泛型将其整合到一起
// 一般使用T这个符号表示类型的变量, 当传的值为100. T就表示number , 同理 传 true ,T表示boolean
// function f13<T>(x: T) {
//     console.log(x);
// }
// f13(100)
// f13('abc')
// f13(true)


// 34. 泛型在接口中的使用
// interface Fn<T> {
//     (m: T): T
// }

// let f15 = <T>(m: T): T => {
//     return m
// }

// console.log(f15(1000));


//35. 泛型在类中的使用
type scopeType = string | number | boolean
// T extends scopeType 泛型约束
class liangshan<T extends scopeType>{
    heroArr: T[] = [];
    push(name: T) {
        this.heroArr.push(name)
    }
}


let shuiboliangshan = new liangshan()
shuiboliangshan.push('晁盖')
shuiboliangshan.push('及时雨-宋江')
shuiboliangshan.push('玉麒麟-卢俊义')
shuiboliangshan.push(true)
// shuiboliangshan.push(null)
// shuiboliangshan.push({})
console.log(shuiboliangshan);

